/*
 * 版权所有：义乌市路比信息科技有限公司
 * 作者：kinwyb<http://www.lcfgly.com>
 * 日期：2015-8-27
 */

package lcfgly.mybatis.auto;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
import lcfgly.mybatis.PageObject;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.logging.log4j.LogManager;

/**
 * 自动分页拦截器
 * @author kinwyb<http://www.lcfgly.com>
 * @datetime 2015-8-27 15:55:18
 * @version 1.0
 */
@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class})})
public class AutoPageInterceptor implements Interceptor {

    org.apache.logging.log4j.Logger logger=LogManager.getLogger("SQL");
    
    String SqlPatten = ".*page$";
    boolean debug = false;

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if(debug)
              logger.debug("PageInterceptor intercept : ===================");
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
        MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
        // 只重写需要分页的sql语句。通过MappedStatement的ID匹配，默认重写以Page结尾的  
        if (mappedStatement.getId().toLowerCase().matches(SqlPatten)) {
            BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
            Object parameterObject = boundSql.getParameterObject();
            if (parameterObject == null) {
                throw new NullPointerException("parameterObject is null!");
            } else {
                // 分页参数作为参数对象parameterObject的一个属性
                String sql = boundSql.getSql();
                if(null != sql && !"".equals(sql.trim())){  //当sql语句不为空执行分页
                    PageObject page = (PageObject) metaStatementHandler.getValue("delegate.boundSql.parameterObject.page");
                    // 重写sql  
                    String pageSql = buildPageSql(sql, page);
                     if(debug)
                        logger.debug("newsql : "+sql);
                    metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
                    metaStatementHandler.setValue("delegate.rowBounds.offset", RowBounds.NO_ROW_OFFSET);
                    metaStatementHandler.setValue("delegate.rowBounds.limit", RowBounds.NO_ROW_LIMIT);
//                    Connection connection = (Connection) invocation.getArgs()[0];
                    // 重设分页参数里的总页数等  
//                    setPageParameter(sql, connection, mappedStatement, boundSql, page);
                }
                
            }
        }
        if(debug)
              logger.debug("End PageInterceptor intercept : ===================");
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }

    @Override
    public void setProperties(Properties properties) {
        if (properties.containsKey("sqlPatten") && !properties.getProperty("sqlPatten").isEmpty()) {
            SqlPatten = properties.getProperty("sqlPatten");
        }
        if(properties.containsKey("debug") && !properties.getProperty("debug").isEmpty()){
            debug=Boolean.parseBoolean(properties.getProperty("debug"));
        }
    }

    /**
     * 生成分页语句
     *
     * @param sql
     * @param page
     * @return
     */
    private String buildPageSql(String sql, PageObject page) {
        if (page != null) {
            StringBuilder pageSql = new StringBuilder();
            long currentpage = page.getNowpage() - 1 > 0 ? page.getNowpage() - 1 : 0;
            pageSql.append(sql);
            pageSql.append(" LIMIT ").append(currentpage * page.getPagecount()).append(" , ").append(page.getPagecount());
            return pageSql.toString();
        } else {
            return sql;
        }
    }

    /**
     * 从数据库里查询总的记录数并计算总页数，回写进分页参数<code>PageParameter</code>,这样调用 者就可用通过 分页参数<code>PageParameter</code>获得相关信息。
     *
     * @param sql
     * @param connection
     * @param mappedStatement
     * @param boundSql
     * @param page
     */
    private void setPageParameter(String sql, Connection connection, MappedStatement mappedStatement, BoundSql boundSql, PageObject page) {
        // 记录总记录数  
        String countSql = "select count(0) from (" + sql + ") as total";
        PreparedStatement countStmt = null;
        ResultSet rs = null;
        try {
            countStmt = connection.prepareStatement(countSql);
            BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql,
                    boundSql.getParameterMappings(), boundSql.getParameterObject());
            setParameters(countStmt, mappedStatement, countBS, boundSql.getParameterObject());
            rs = countStmt.executeQuery();
            int totalCount = 0;
            if (rs.next()) {
                totalCount = rs.getInt(1);
            }
            if(debug)
                logger.debug("rows : "+totalCount);
            page.setRows(totalCount);
            long totalPage = totalCount / page.getPagecount() + ((totalCount % page.getPagecount() == 0) ? 0 : 1);
            page.setPages(totalPage);
        } catch (SQLException e) {
            logger.warn(" 设置分页数据出错 ：",e);
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (countStmt != null) {
                    countStmt.close();
                }
            } catch (SQLException e) {
                logger.warn(" 设置分页数据出错 ：",e);
            }
        }
    }

    /**
     * 对SQL参数(?)设值
     *
     * @param ps
     * @param mappedStatement
     * @param boundSql
     * @param parameterObject
     * @throws SQLException
     */
    private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql,
            Object parameterObject) throws SQLException {
        ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
        parameterHandler.setParameters(ps);
    }

}
